package com.hj.springframework2.context;

import com.hj.springframework2.exception.BeanCreationException;
import lombok.Data;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @Author : hujing
 * @Date: 2025/6/13 14:40
 * @Description:
 */
@Data
public class BeanDefinition implements Comparable<BeanDefinition> {

    private final String name;//bean的名称
    private final Class<?> beanClass;//bean的声明类型
    private Object instance = null;//bean的实例
    private final Constructor<?> constructor;//构造方法
    private String factoryName;//工厂方法名称
    private Method factoryMethod;//工厂方法
    private final int order;//排序
    private final boolean isPrimary;//是否是主要的bean

    private String initMethodName;//初始化方法名称
    private String destroyMethodName;//销毁方法名称

    private Method initMethod;//初始化方法
    private Method destroyMethod;//销毁方法

    public BeanDefinition(String name, Class<?> beanClass, Constructor<?> constructor, int order, boolean isPrimary, String initMethodName, String destroyMethodName, Method initMethod, Method destroyMethod) {
        this.name = name;
        this.beanClass = beanClass;
        this.constructor = constructor;
        this.order = order;
        this.isPrimary = isPrimary;
        constructor.setAccessible(true);
        setInitAndDestroyMethod(initMethodName, destroyMethodName, initMethod, destroyMethod);
    }

    public BeanDefinition(String name, Class<?> beanClass, String factoryName, Method factoryMethod, int order, boolean primary, String initMethodName,
                          String destroyMethodName, Method initMethod, Method destroyMethod) {
        this.name = name;
        this.beanClass = beanClass;
        this.constructor = null;
        this.order = order;
        this.isPrimary = primary;
        this.factoryMethod = factoryMethod;
        this.factoryName = factoryName;
        factoryMethod.setAccessible(true);
        setInitAndDestroyMethod(initMethodName, destroyMethodName, initMethod, destroyMethod);
    }

    private void setInitAndDestroyMethod(String initMethodName, String destroyMethodName, Method initMethod, Method destroyMethod) {
        this.initMethodName = initMethodName;
        this.destroyMethodName = destroyMethodName;
        if (initMethod != null) {
            initMethod.setAccessible(true);
        }
        if (destroyMethod != null) {
            destroyMethod.setAccessible(true);
        }
        this.initMethod = initMethod;
        this.destroyMethod = destroyMethod;
    }

    @Override
    public String toString() {
        return "BeanDefinition{" +
                "name='" + name + '\'' +
                ", beanClass=" + beanClass +
                ", instance=" + instance +
                ", constructor=" + constructor +
                ", factoryName='" + factoryName + '\'' +
                ", factoryMethod=" + factoryMethod +
                ", order=" + order +
                ", isPrimary=" + isPrimary +
                ", initMethodName='" + initMethodName + '\'' +
                ", destroyMethodName='" + destroyMethodName + '\'' +
                ", initMethod=" + initMethod +
                ", destroyMethod=" + destroyMethod +
                '}';
    }

    @Override
    public int compareTo(BeanDefinition def) {
        int cmp = Integer.compare(this.order, def.order);
        if (cmp != 0) {
            return cmp;
        }
        return this.name.compareTo(def.name);
    }

    public Object getRequiredInstance() {
        if (this.instance == null){
            throw new BeanCreationException(String.format("Instance of bean with name '%s' and type '%s' is not instantiated during current stage.",
                    this.getName(), this.getBeanClass().getName()));
        }
        return this.instance;
    }
}
